/**
 * @file usermenu_update_image.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-09-27
 *
 * @copyright Copyright (c) 2022
 *
 */
#include <linux/stddef.h>
#include <string.h>
#include <image.h>
#include <net.h>
#include <mmc.h>
#include <malloc.h>
#include <dm/uclass.h>
#include <part.h>

#ifdef CONFIG_TARGET_MX6ULL_14X14_ALPHA
#include <asm/mach-imx/boot_mode.h>
#endif
#include <asm/arch/sys_proto.h>
#include "usermenu_update_image.h"

extern ulong image_load_addr;

static UPDATE_INFO_ST g_update_info;
static int mmc_info_init = 0;

/**
 * @brief 检查更新设备的类型
 *
 * @param update_dev 	更新设备的枚举类型
 * @return int			成功返回 0；失败返回 -1
 */
static int check_update_dev(BLK_DEV_TYPE_ET update_dev)
{
	switch (update_dev) {
		case BLK_DEV_SD:
		case BLK_DEV_EMMC:
		case BLK_DEV_NAND_FLASH:
		case BLK_DEV_NOR_FLASH:
			g_update_info.update_dev_type = update_dev;
			break;
		default:
			return -1;
	}
	return 0;
}

/**
 * @brief 更新镜像名到全局变量中
 *
 * @param env
 * @return int
 */
static int get_image_name(const char *env)
{
	char *s = NULL;
	if(NULL == env) return	-1;

	if(!strncmp(env, "u-boot", strlen(env))) {
		s = env_get("u-boot");
		if((NULL != s) &&
			!strncmp(g_update_info.uboot, s, strlen(s) + 1)) {
			/** 更新全局变量中的 u-boot 变量名 */
			memset(g_update_info.uboot, 0, strlen(g_update_info.uboot));
			strncpy(g_update_info.uboot, s, strlen(s) + 1);
		}

		return 0;
	}

	if(!strncmp(env, "fdt_file", strlen(env))) {
		s = env_get("fdt_file");
		if((NULL != s) &&
			!strncmp(g_update_info.dtb, s, strlen(s) + 1)) {
			/** 更新全局变量中的 u-boot 变量名 */
			memset(g_update_info.dtb, 0, strlen(g_update_info.dtb));
			strncpy(g_update_info.dtb, s, strlen(s) + 1);
		}

		return 0;
	}

	if(!strncmp(env, "boot_file", strlen(env))) {
		s = env_get("boot_file");
		if((NULL != s) &&
			!strncmp(g_update_info.kernel, s, strlen(s) + 1)) {
			/** 更新全局变量中的 u-boot 变量名 */
			memset(g_update_info.kernel, 0, strlen(g_update_info.kernel));
			strncpy(g_update_info.kernel, s, strlen(s) + 1);
		}

		return 0;
	}

	if(!strncmp(env, "rootfs_file", strlen(env))) {
		s = env_get("rootfs_file");
		if((NULL != s) &&
			!strncmp(g_update_info.rootfs, s, strlen(s + 1))) {
			/** 更新全局变量中的 u-boot 变量名 */
			memset(g_update_info.rootfs, 0, strlen(g_update_info.rootfs));
			strncpy(g_update_info.rootfs, s, strlen(s) + 1);
		}

		return 0;
	}

	if(!strncmp(env, "image", strlen(env))) {
		s = env_get("image");
		if((NULL != s) &&
			!strncmp(g_update_info.image, s, strlen(s) + 1)) {
			/** 更新全局变量中的 u-boot 变量名 */
			memset(g_update_info.image, 0, strlen(g_update_info.image));
			strncpy(g_update_info.image, s, strlen(s) + 1);
		}

		return 0;
	}

	return 0;
}

/**
 * @brief 初始化要下载的镜像名
 * 
 * @param image_type 镜像类型
 * @return int 成功返回 0,失败返回 -1
 */
static int check_image(UPDATE_IMAGE_TYPE_ET image_type)
{
	switch (image_type) {
		case IMAGE_IS_UBOOT:
			return get_image_name("u-boot");
		case IMAGE_IS_DTB:
			return get_image_name("fdt_file");
		case IMAGE_IS_KERNEL:
			return get_image_name("boot_file");
		case IMAGE_IS_ROOTFS:
			return get_image_name("rootfs_file");
		case IMAGE_IS_ALL:
			return get_image_name("image");
		default:
			return -1;
	}

	g_update_info.update_image = image_type;

	return 0;
}

/**
 * @brief  设置下载镜像后存放的内存地址
 *
 * @return int 成功返回 0，失败返回 -1
 */
static int set_load_addr(void)
{
	int ret = -1;

	switch (g_update_info.update_image) {
		case IMAGE_IS_UBOOT:
		case IMAGE_IS_KERNEL:
		case IMAGE_IS_ROOTFS:
		case IMAGE_IS_ALL:
			image_load_addr = g_update_info.load_addr;
			ret = 0;
			break;
		case IMAGE_IS_DTB:
			image_load_addr = g_update_info.fdt_addr;
			ret = 0;
			break;
		default:
			ret = -1;
			break;
	}

	return ret;
}
#if 0
/**
 * @brief 
 * 
 * @return int 
 */
static int mmc_dev_init_for_update(void)
{
	int i = 0;
	struct udevice *dev = NULL;
	struct mmc *mmc_dev = NULL;
	bool force_init = false;
	char *mmc_dev_name = NULL;
	BLK_DEV_TYPE_ET mmc_dev_type;
	int ret = -1;

	for (i = 0; ; i++) {
		ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev);
		if (ret == -ENODEV) {
			printf("[%s]: uclass get device failed, break %d.\n", __func__, i);
			break;
		}

		mmc_dev = find_mmc_device(i);
		if(NULL == mmc_dev) {
			printf("[%s]: can not find mmc device, i = %d.\n", __func__, i);
			continue;
		}

		/** 检查 CD 引脚是否插入 */
		if (!mmc_getcd(mmc_dev))
			force_init = true;

		if (force_init)
			mmc_dev->has_init = 0;

		if (mmc_init(mmc_dev)) {
			printf("[%s]: mmc init failed, i = %d.\n", __func__, i);
			continue;
		}

		mmc_dev_name = IS_SD(mmc_dev) ? "SD" : "eMMC";
		if(!strncmp(mmc_dev_name, "SD", 2)) {
			mmc_dev_type = BLK_DEV_SD;
		}
		else if(!strncmp(mmc_dev_name, "eMMC", 4)) {
			mmc_dev_type = BLK_DEV_EMMC;
		}
		else {
			mmc_dev_name = NULL;
			continue;
		}

		/** 检查 mmc 设备是否是要升级的 mmc 设备 */
		if(g_update_info.update_dev_type != mmc_dev_type) {
			mmc_dev_name = NULL;
			continue;
		}

		g_update_info.mmc_dev_num = mmc_get_blk_desc(mmc_dev)->devnum;

		switch(g_update_info.update_image) {
			case IMAGE_IS_UBOOT:
				if(BLK_DEV_SD == g_update_info.update_dev_type)
					g_update_info.update_part = SD_PARTTION_BOOT0;
				else if (BLK_DEV_EMMC == g_update_info.update_dev_type)
					g_update_info.update_part = EMMC_PARTTION_BOOT0;
				else {
					printf("Unkowned boot part.\n");
					return -1;
				}
				break;
			case IMAGE_IS_DTB:
			case IMAGE_IS_KERNEL:
				g_update_info.update_part = MMC_PARTTION_KERNEL;
				break;
			case IMAGE_IS_ROOTFS:
				g_update_info.update_part = MMC_PARTTION_ROOTFS;
				break;
			case IMAGE_IS_ALL:
				break;
			default:
				return -1;
		}
	}

	return 0;
}
#endif

#if 0
/**
 * @brief  对要更新的存储设备进行初始化
 * 
 * @return int 成功返回 0, 失败返回 -1
 */
static int update_image_init_dev_for_upgrade(void)
{
	switch(g_update_info.update_dev_type) {
		case BLK_DEV_SD:
		case BLK_DEV_EMMC:
			return mmc_dev_init_for_update();
		case BLK_DEV_NOR_FLASH:
		case BLK_DEV_NAND_FLASH:
		case BLK_DEV_SSD:
		default:
			return -1;
	}

	return 0;
}
#endif
/**
 * @brief  从网络上获取镜像
 * @note   net_boot_file_name 是 net/net.c 中的全局变量，存储要下载的文件名
 * @param  file_size: 	输出，下载的文件的大小
 * @retval 成功返回传输的文件的大小，失败返回 -1
 */
static int fetch_image_file_from_net(void)
{
	int file_size = 0;

	switch(g_update_info.update_image) {
		case IMAGE_IS_UBOOT:
			copy_filename(net_boot_file_name, g_update_info.uboot, sizeof(net_boot_file_name));
			break;
		case IMAGE_IS_DTB:
			copy_filename(net_boot_file_name, g_update_info.kernel, sizeof(net_boot_file_name));
			break;
		case IMAGE_IS_KERNEL:
			copy_filename(net_boot_file_name, g_update_info.dtb, sizeof(net_boot_file_name));
			break;
		case IMAGE_IS_ROOTFS:
			copy_filename(net_boot_file_name, g_update_info.rootfs, sizeof(net_boot_file_name));
			break;
		case IMAGE_IS_ALL:
			copy_filename(net_boot_file_name, g_update_info.image, sizeof(net_boot_file_name));
			break;
		default:
			printf("Unknown image type.\n");
			return -1;
	}

	/** u-boot 通过网络有两种方式下载镜像：dhcp 和 tftp */

	file_size = net_loop(TFTPGET);
	if(file_size <= 0) return -1;
	g_update_info.update_image_size = file_size;

	return 0;
}

/**
 * @brief mmc 更新 u-boot
 *
 * @param devno 	 	mmc 设备号
 * @param partition 	要更新的分区
 * @param filesize 		要更新的镜像文件的大小
 * @return int 			0 on success, or != 0 on error
 */
static int mmc_update_image_uboot(void)
{
	char cmd[256] = {0};
	int ret = 0;
	int bootsize = 0;

	/** 计算要烧写的大小，512 是一个块的大小 */
	bootsize = g_update_info.update_image_size / 512;

	switch(g_update_info.update_dev_type) {
		case BLK_DEV_SD:
			snprintf(cmd, sizeof(cmd), "mmc dev %d %d\n",
						g_update_info.sd_blk_info.mmc_dev_num, 
						g_update_info.sd_blk_info.bootloader_part_num0);
			ret = run_command(cmd, 0);	///< 切换到 EMMC 的分区 0，即 boot0 分区
			if(0 != ret) return ret;

			memset(cmd, 0, sizeof(cmd));
			/** 从 SD 分区 0 第 2 个块开始烧写新的 uboot，前 2 个块存放分区表 */
			snprintf(cmd, sizeof(cmd), "mmc write %x %d %x\n", 
						(unsigned int)g_update_info.load_addr, 
						g_update_info.sd_blk_info.bootloader_sector_offset,
						bootsize);
			ret = run_command(cmd, 0); ///< 烧写 u-boot.imx 到 EMMC 中
			if(0 != ret) return ret;
			break;
		case BLK_DEV_EMMC:
			snprintf(cmd, sizeof(cmd), "mmc dev %d %d\n",
						g_update_info.eMMC_blk_info.mmc_dev_num, 
						g_update_info.eMMC_blk_info.default_bootloader);
			ret = run_command(cmd, 0);	///< 切换到 EMMC 的 boot 分区 0，即 boot0 或 boot1 分区
			if(0 != ret) return ret;

			memset(cmd, 0, sizeof(cmd));
			/** 从 eMMC 分区 boot0 或 boot1 第 2 个块开始烧写新的 uboot，前 2 个块存放分区表 */
			snprintf(cmd, sizeof(cmd), "mmc write %x %d %x\n", 
						(unsigned int)g_update_info.load_addr, 
						g_update_info.eMMC_blk_info.bootloader_sector_offset,
						bootsize);
			ret = run_command(cmd, 0); ///< 烧写 u-boot.imx 到 EMMC 中
			if(0 != ret) return ret;

			/** eMMC 烧写完成后，需要执行 mmc partconf 命令来设置新的启动分区 */
			memset(cmd, 0, sizeof(cmd));
			snprintf(cmd, sizeof(cmd), "mmc partconf %d %d %d %d\n",
						g_update_info.eMMC_blk_info.mmc_dev_num,
						1,
						g_update_info.eMMC_blk_info.default_bootloader,
						g_update_info.eMMC_blk_info.default_bootloader);
			ret = run_command(cmd, 0); ///< 烧写 u-boot.imx 到 EMMC 中
			if(0 != ret) return ret;
			break;
		default:
			return -1;
	}

	return ret;
}

/**
 * @brief 
 * 
 * @return int 
 */
static int mmc_update_image_fdt(void)
{
	int ret = 0;
	char cmd[256] = {0};

	memset(cmd, '\0', sizeof(cmd));
	switch(g_update_info.update_dev_type) {
		case BLK_DEV_SD:
			/** 把内存上的数据存储到 fat 分区的一个文件里 */
			snprintf(cmd, sizeof(cmd), "fatwrite mmc %d:%d %x ${fdt_file} %x\n",
						g_update_info.sd_blk_info.mmc_dev_num, 
						g_update_info.sd_blk_info.boot_part_num, 
						(unsigned int)image_load_addr, 
						g_update_info.update_image_size);
			break;
		case BLK_DEV_EMMC:
			/** 把内存上的数据存储到 fat 分区的一个文件里 */
			snprintf(cmd, sizeof(cmd), "fatwrite mmc %d:%d %x ${fdt_file} %x\n",
						g_update_info.eMMC_blk_info.mmc_dev_num, 
						g_update_info.eMMC_blk_info.boot_part_num, 
						(unsigned int)image_load_addr, 
						g_update_info.update_image_size);
			break;
		default:
			return -1;
	}
	ret = run_command(cmd, 0);

	return ret;
}

/**
 * @brief 
 * 
 * @return int 
 */
static int mmc_update_image_kernel(void)
{
	int ret = 0;
	char cmd[256] = {0};

	memset(cmd, '\0', sizeof(cmd));
	switch(g_update_info.update_dev_type) {
		case BLK_DEV_SD:
			/** 把内存上的数据存储到 fat 分区的一个文件里 */
			snprintf(cmd, sizeof(cmd), "fatwrite mmc %d:%d %x ${boot_file} %x\n",
						g_update_info.sd_blk_info.mmc_dev_num, 
						g_update_info.sd_blk_info.boot_part_num, 
						(unsigned int)image_load_addr, 
						g_update_info.update_image_size);
			break;
		case BLK_DEV_EMMC:
			/** 把内存上的数据存储到 fat 分区的一个文件里 */
			snprintf(cmd, sizeof(cmd), "fatwrite mmc %d:%d %x ${boot_file} %x\n",
						g_update_info.eMMC_blk_info.mmc_dev_num, 
						g_update_info.eMMC_blk_info.boot_part_num, 
						(unsigned int)image_load_addr, 
						g_update_info.update_image_size);
			break;
		default:
			return -1;
	}

	ret = run_command(cmd, 0);

	return ret;
}

/**
 * @brief 
 * 
 * @return int 
 */
static int mmc_update_image_rootfs(void)
{
	int ret = 0;
	char cmd[256] = {0};

	memset(cmd, '\0', sizeof(cmd));
	switch(g_update_info.update_dev_type) {
		case BLK_DEV_SD:
			snprintf(cmd, sizeof(cmd), "ext4write mmc %d:%d %x ${rootfs_file} %x\n",
						g_update_info.sd_blk_info.mmc_dev_num, 
						g_update_info.sd_blk_info.rootfs_part_num, 
						(unsigned int)image_load_addr, 
						g_update_info.update_image_size);
			break;
		case BLK_DEV_EMMC:
			snprintf(cmd, sizeof(cmd), "ext4write mmc %d:%d %x ${rootfs_file} %x\n",
						g_update_info.eMMC_blk_info.mmc_dev_num, 
						g_update_info.eMMC_blk_info.rootfs_part_num, 
						(unsigned int)image_load_addr, 
						g_update_info.update_image_size);
			break;
		default:
			return -1;
	}
	/** 切换到文件系统分区 */
	ret = run_command(cmd, 0);
	if(ret != 0) {
		return -1;
	}

	return 0;
}

/**
 * @brief 
 * 
 * @return int 
 */
static int mmc_update_image_all(void)
{
	switch(g_update_info.update_dev_type) {
		case BLK_DEV_SD:
			break;
		case BLK_DEV_EMMC:
			break;
		default:
			return -1;
	}
	return 0;
}

/**
 * @brief  烧写镜像
 * 
 * @return int 成功返回 0，失败返回 -1
 */
static int mmc_update_image_proc(void)
{
	switch (g_update_info.update_image) {
		case IMAGE_IS_UBOOT:	///< 更新 u-boot
			return mmc_update_image_uboot();
		case IMAGE_IS_DTB:		///< 更新设备树
			return mmc_update_image_fdt();
		case IMAGE_IS_KERNEL:	///< 更新 kernel
			return mmc_update_image_kernel();
		case IMAGE_IS_ROOTFS:	///< 更新文件系统
			return mmc_update_image_rootfs();
		case IMAGE_IS_ALL:		///< 更新打包的整体镜像
			return mmc_update_image_all();
		default:
			return -1;
	}

	return 0;
}

/**
 * @brief  烧写镜像开始
 * 
 * @return int 
 */
static int update_image_start(void)
{
	switch (g_update_info.update_dev_type) {
		case BLK_DEV_SD:
		case BLK_DEV_EMMC:
			return mmc_update_image_proc();
		case BLK_DEV_NOR_FLASH:
		case BLK_DEV_NAND_FLASH:
		case BLK_DEV_SSD:
		default:
			return -1;
	}

	return 0;
}

#if CONFIG_TARGET_MX6ULL_14X14_ALPHA

static const char *mmc_get_dev_name(int mmc_dev_num)
{
	struct mmc *mmc;

	mmc = find_mmc_device(mmc_dev_num);
	if (!mmc) {
		printf("no mmc device in slot %d.\n", mmc_dev_num);
		return NULL;
	}

	return IS_SD(mmc) ? "SD" : "eMMC";
}

static int __mmc_part_info_get(const char *part_name, 
									struct blk_desc **fs_dev_desc, 
									struct disk_partition *fs_partition)
{
	int ret;

	ret = part_get_info_by_dev_and_name_or_num("mmc", part_name, fs_dev_desc, fs_partition, true);
	if (ret < 0) {
		printf("get mmc part info error.\n");
		return -1;
	}

	return 0;
}

static int mmc_part_info_get(int dev_num, int part_num, struct disk_partition *disk_part_info)
{
	int ret = -1;
	struct blk_desc *fs_dev_desc;
	struct disk_partition fs_partition;
	char part_name[8];

	snprintf(part_name, sizeof(part_name), "%d:%d", dev_num, part_num);
	ret = __mmc_part_info_get(part_name, &fs_dev_desc, &fs_partition);
	if(ret < 0) {
		printf("get mmc part info error.\n");
		return -1;
	}

	memcpy(disk_part_info, &fs_partition, sizeof(fs_partition));

	return 0;
}

static int mmc_sd_part_info_get(int dev_num)
{
	struct disk_partition disk_part_info;
	int ret = -1;

	/** 需要注意的是 0 号分区通过此操作获取不到 */
	g_update_info.sd_blk_info.bootloader_part_num0 = 0;
	g_update_info.sd_blk_info.bootloader_sector_start = 0;
	g_update_info.sd_blk_info.bootloader_sector_offset = 2;

	/** 获取 1 号分区信息 */
	ret = mmc_part_info_get(dev_num, 1, &disk_part_info);
	if(ret < 0) {
		printf("SD Card get part 1 info error.");
		return -1;
	}
	g_update_info.sd_blk_info.boot_sector_start = disk_part_info.start;
	g_update_info.sd_blk_info.boot_part_num = 1;

	/** 获取 2 号分区信息 */
	memset(&disk_part_info, 0, sizeof(disk_part_info));
	ret = mmc_part_info_get(dev_num, 2, &disk_part_info);
	if(ret < 0) {
		printf("SD Card get part 2 info error.");
		return -1;
	}
	g_update_info.sd_blk_info.boot_sector_start = disk_part_info.start;
	g_update_info.sd_blk_info.boot_part_num = 2;

	return 0;
}

static int mmc_emmc_part_info_get(int dev_num)
{
	struct disk_partition disk_part_info;
	int ret = -1;

	/** 需要注意的是 0 或 1 号分区通过此操作获取不到 */
	g_update_info.eMMC_blk_info.bootloader_part_num0 = 0;
	g_update_info.eMMC_blk_info.bootloader_part_num1 = 1;
	g_update_info.eMMC_blk_info.bootloader_sector_start = 0;
	g_update_info.eMMC_blk_info.bootloader_sector_offset = 2;
	g_update_info.eMMC_blk_info.default_bootloader = 0;

	/** 获取 1 号分区信息 */
	ret = mmc_part_info_get(dev_num, 1, &disk_part_info);
	if(ret < 0) {
		printf("SD Card get part 1 info error.");
		return -1;
	}
	g_update_info.eMMC_blk_info.boot_sector_start = disk_part_info.start;
	g_update_info.eMMC_blk_info.boot_part_num = 1;

	/** 获取 2 号分区信息 */
	memset(&disk_part_info, 0, sizeof(disk_part_info));
	ret = mmc_part_info_get(dev_num, 2, &disk_part_info);
	if(ret < 0) {
		printf("SD Card get part 2 info error.");
		return -1;
	}
	g_update_info.eMMC_blk_info.boot_sector_start = disk_part_info.start;
	g_update_info.eMMC_blk_info.boot_part_num = 2;

	return 0;
}

/**
 * @brief  对 MMC 设备进行初始化, 针对 alpha 开发版只有两个 mmc 设备: SD 和 eMMC,
 * 		   所以设备编号只有 0 和 1
 * 
 * @return int 
 */
static int mmc_part_info_init(void)
{
	char *mmc_name = NULL;
	int i = 0;
	int ret = -1;

	/** 获取 MMC 设备名称 */
	for(i = 0; i <= 1; i++) {
		mmc_name = mmc_get_dev_name(i);
		if(NULL == mmc_name)
			continue;
		if(!strncmp(mmc_name, "SD", 2)) {
			g_update_info.sd_blk_info.mmc_dev_num = i;
			g_update_info.sd_blk_info.mmc_name = "SD";
			ret = mmc_sd_part_info_get(g_update_info.sd_blk_info.mmc_dev_num);
			if(ret < 0)
				return -1;
		}

		if(!strncmp(mmc_name, "eMMC", 4)) {
			g_update_info.eMMC_blk_info.mmc_dev_num = i;
			g_update_info.eMMC_blk_info.mmc_name = "eMMC";
			ret = mmc_emmc_part_info_get(g_update_info.eMMC_blk_info.mmc_dev_num);
			if(ret < 0)
				return -1;
		}
	}

	if(g_update_info.boot_dev == BLK_DEV_SD) {
		g_update_info.eMMC_blk_info.is_boot_dev = 0;
		g_update_info.sd_blk_info.is_boot_dev = 1;
	}
	else if(g_update_info.boot_dev == BLK_DEV_EMMC) {
		g_update_info.eMMC_blk_info.is_boot_dev = 1;
		g_update_info.sd_blk_info.is_boot_dev = 0;
	}
	else {
		g_update_info.eMMC_blk_info.is_boot_dev = 0;
		g_update_info.sd_blk_info.is_boot_dev = 0;
	}
	
	return 0;
}

#endif

/**
 * @brief 升级镜像的函数
 *
 * @param update_dev 要升级的镜像所在的块设备类型
 * @param image_type 要升级的镜像类型
 * @return int 	  	 成功返回 0，事变返回 -1
 */
int usermenu_update_image_proc(BLK_DEV_TYPE_ET update_dev_type, UPDATE_IMAGE_TYPE_ET image_type)
{
	int ret = -1;

	/** 检查要升级的块设备 */
	ret = check_update_dev(update_dev_type);
	if(ret < 0) {
		printf("Device type is Unkonwned.\n");
		goto ERROR_OUT;
	}

	/** 检查镜像是否 OK */
	ret = check_image(image_type);
	if(ret < 0) {
		printf("Image is Unkonwned.\n");
		goto ERROR_OUT;
	}

	/** 设置镜像下载到内存中的地址 */
	ret = set_load_addr();
	if(ret < 0) {
		printf("Load addr is Unkowned.\n");
		goto ERROR_OUT;
	}

#if 0
	/** 初始化要镜像要更新的设备 */
	ret = update_image_init_dev_for_upgrade();
	if(ret < 0) {
		printf("Init dev failed, please check your block device.\n");
		goto ERROR_OUT;
	}
#endif
	/** 从网络上获取镜像 */
	ret = fetch_image_file_from_net();
	if(ret < 0) {
		printf("Fetch image file failed.\n");
		goto ERROR_OUT;
	}

	/** 烧写镜像 */
	ret = update_image_start();
	if(ret < 0) {
		printf("Update image failed.\n");
		goto ERROR_OUT;
	}

	return 0;
ERROR_OUT:
	return -1;
}

/**
 * @brief 
 * 
 */
static void image_loadaddr_get(void)
{
    char *s = NULL;

    s = env_get("loadaddr");
    if(NULL == s) {
        g_update_info.load_addr = DEFAULT_SYS_LOAD_ADDR;
	}
    else {
        g_update_info.load_addr  = simple_strtoul(s, NULL, 16);
	}

	s = NULL;
    s = env_get("fdt_addr");
    if(NULL == s) {
        g_update_info.fdt_addr  = DEFAULT_FDT_LOAD_ADDR;
	}
    else {
        g_update_info.fdt_addr  = simple_strtoul(s, NULL, 16);
	}
}

/**
 * @brief 
 * 
 * @return int 
 */
static int get_image_boot_dev(void)
{
	switch (get_boot_device()) {
		case SD1_BOOT:
		case SD2_BOOT:
		case SD3_BOOT:
			g_update_info.boot_dev = BLK_DEV_SD;
			break;
		case MMC1_BOOT:
		case MMC2_BOOT:
		case MMC3_BOOT:
			g_update_info.boot_dev = BLK_DEV_EMMC;
			break;
		default:
			printf("ERROR:  unsupported device\n");
			return -1;
	}

	return 0;
}

/**
 * @brief
 *
 * @return int  0   表示初始化成功
 *              1   表示已经初始化过了
 *              -1  出错
 */
int update_image_init(void)
{
	int ret = -1;
	char *s;

	s = env_get("u-boot");
	if(NULL == s) {
		s = DEFAULT_BOOT_FILENAME;
		env_set("u-boot", s);
	}
	strncpy(g_update_info.uboot, s, strlen(s) + 1);

	s = env_get("boof_file");
	if(NULL == s) {
		s = DEFAULT_FDT_FILENAME;
		env_set("boof_file", s);
	}
	strncpy(g_update_info.kernel, s, strlen(s) + 1);

	s = env_get("fdt_file");
	if(NULL == s) {
		s= DEFAULT_KERNEL_FILENAME;
		env_set("fdt_file", s);
	}
	strncpy(g_update_info.dtb, s, strlen(s) + 1);

	s = env_get("rootfs_file");
	if(NULL ==s) {
		s = DEFAULT_ROOTFS_EXT4_FILENAME;
		env_set("rootfs_file", s);
	}
	strncpy(g_update_info.rootfs, s, strlen(s) + 1);

	s = env_get("image");
	if(NULL == s) {
		s = DEFAULT_ALL_FILENAME;
		env_set("image", s);
	}
	strncpy(g_update_info.image, s, strlen(s) + 1);

	/** 获取系统加载地址和 fdt 地址 */
	image_loadaddr_get();

	/** 获取当前的启动设备类型 */
	ret = get_image_boot_dev();
    if(ret < 0) {
		printf("[%s]: get boot device error.\n", __func__);
		goto ERROR_OUT;
	}

	if(mmc_info_init != 1) {
		ret = mmc_part_info_init();
		if(ret < 0) {
			goto ERROR_OUT;
		}
		mmc_info_init = 1;
	}

    return 0;
ERROR_OUT:
	return -1;
}